home *** CD-ROM | disk | FTP | other *** search
/ Games of Daze / Infomagic - Games of Daze (Summer 1995) (Disc 1 of 2).iso / x2ftp / msdos / libs / actlib17 / dbgalloc.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-09-03  |  12.0 KB  |  468 lines

  1. /************************************************************
  2.  
  3.     DBGALLOC.C
  4.  
  5.     Written by Marc Stern, based on an initial idea 
  6.                            from Piet Honkoop.
  7.                            
  8.  
  9.     Modifications in malloc(), realloc & free()
  10.     to perform intensive consistency check.
  11.  
  12.     To use this object, just link it with your application
  13.     BEFORE standard run-time lib.
  14.  
  15.     Warning:  - This can only be used in large & huge models.
  16.               - You cannot use _fmalloc()/farmalloc(), 
  17.                                _frealloc()/farrealloc() 
  18.                                _ffree()/farfree()
  19.                                _fcalloc()/farcalloc()
  20.                                fstrdup()
  21.               - You will get three warnings from the linker
  22.                 (duplicate symbol). This is normal.
  23.  
  24.  
  25.   Modifications to normal functions:
  26.   ---------------------------------
  27.  
  28.     Allocated blocks are leaded and trailed by a block
  29.     of the form:
  30.  
  31.        <total size of the block> (2  bytes)
  32.        <number of the block>     (2  bytes)
  33.        '\xA6' ... '\xA6'         (8  bytes)
  34.        <number of the block>     (2  bytes)
  35.        <total size of the block> (2  bytes)
  36.  
  37.     Redundoncy is needed because the size could
  38.     be overwritten if error.
  39.  
  40.  
  41.     All allocated blocks are stored in a structure.
  42.     This allow you to check from your application
  43.     all the previously allocated blocks.
  44.     You just have to call the function
  45.  
  46.         void allocCheck( void );         
  47.  
  48.     A good place to call allocCheck() would be in an idle loop
  49.     in your application (if any).
  50.     ex. in TurboVision: the TApplication::idle() function.
  51.  
  52. ************************************************************/
  53.  
  54.  
  55.  
  56. #if defined( NDEBUG )
  57. #undef NDEBUG
  58. #endif
  59.  
  60. #include <assert.h>
  61. #include <malloc.h>
  62. #include <stdlib.h>
  63. #include <string.h>
  64. #include <stdio.h>
  65. #include <conio.h>
  66. #include <dos.h>
  67. #include <io.h>
  68. #include <ctype.h>
  69.  
  70. #if defined(_MSC_VER)
  71. # define heapOK()     ( _heapchk() == _HEAPOK )
  72. #else
  73. # define heapOK()     ( heapcheck() >= 0 )
  74. #endif
  75.  
  76. #define uchar     unsigned char
  77. #define ulong     unsigned long
  78.  
  79. #define pad_size  8
  80.  
  81. /*
  82.  This structure represents the block that will enclose
  83.  all allocated ones.
  84. */
  85. typedef struct { size_t size1;
  86.                  size_t nb1;
  87.                  uchar  padding[pad_size];
  88.                  size_t size2;
  89.                  size_t nb2;
  90.                }
  91.                Header;
  92.                   
  93. const uchar BLK_DATA = 0xA6;
  94.  
  95.  
  96. /* This allows to check up to 3000 previously allocated blocks */
  97. #define MAXSIZE  3000
  98.  
  99.  
  100. /*
  101.  This structure represents the table that will contain
  102.  all allocated blocks.
  103. */
  104. static struct
  105.        {
  106.         uchar    *contents[MAXSIZE];  /* pointers to all allocated blocks */
  107.         size_t   total;               /* number of currently stored pointers */
  108.         int      inFree;              /* flag to see if we are checking the block to be freeed */
  109.         int      inError;             /* flag to see if we are in msg_print function */
  110.                                       /* because Microsoft uses malloc() inside fputs(),... */
  111.         ulong    totalAlloc;          /* current total allocated bytes without the headers */
  112.         ulong    maxAlloc;            /* maximum allocated bytes without the headers */
  113.        } blocks;
  114.  
  115.  
  116. #define BADHEAP     -1
  117. #define NOTFREEED   -2
  118. #define MAXALLOC    -3
  119.  
  120. /*
  121.  Display an error message giving the number of the block (if valid),
  122.  its position (leading/trailing) and its contents.
  123.  If no header given, assumes an error of trying to free a NULL
  124.  pointer (which is actually a warning and not an error .
  125.  
  126.  The user has the choice to ignore (continue), to exit
  127.  the program or to abort it (that means to stop it without
  128.  closing open files, releasing extended/expanded memory,...
  129. */
  130.  
  131. static void msg_print( long blk_nb, int delimiter, Header *header )
  132. {
  133.  FILE *input = NULL;
  134.  long inputTell;
  135.  int i;
  136.  
  137.  blocks.inError = 1;
  138.  
  139.  switch( delimiter )
  140.  {
  141.   case MAXALLOC:
  142.      fprintf( stderr, "\n  Maximum allocated bytes (without headers): %lu\n", blocks.maxAlloc );
  143.      break;
  144.  
  145.   case NOTFREEED:
  146.      fprintf( stderr, "\n  Warning: %d blocks not freeed.\n", blocks.total );
  147.      break;
  148.  
  149.   case BADHEAP:
  150.      fputs( "\n  Error: Heap corrupted.\n", stderr );
  151.      break;
  152.  
  153.   default:
  154.      if ( ! header )
  155.         fputs( "\n  Error: trying to free a NULL pointer.\n", stderr );
  156.      else
  157.  {
  158.  char fmt[255] = "\n"
  159.                  "  Fatal error in function %s() in previously allocated block.\n"
  160.                  "  Error in %sing header";
  161.  if ( blk_nb >= 0 ) strcat( fmt, " - block n. %ld." );
  162.  fprintf( stderr, fmt, blocks.inFree?"free":"allocCheck", delimiter?"trail":"lead", blk_nb );
  163.  
  164.  fprintf( stderr, "\n"
  165.                   "  Header:  size1 = %u, size2 = %u\n"
  166.                   "           number1 = %u, number2 = %u\n"
  167.                   "           padding = [",
  168.                   header->size1, header->size1,
  169.                   header->nb1, header->nb2
  170.       );
  171.  
  172.  for ( i = 0; i < pad_size; i++ )
  173.      fprintf( stderr, " %X", header->padding[i] );
  174.  
  175.  fputs( " ]\n", stderr );
  176.  }
  177.  } /* switch() */
  178.  
  179.  {
  180.   char *msg;
  181.  
  182.   switch( delimiter )
  183.   {
  184.    case NOTFREEED:
  185.       msg = NULL;
  186.       break;
  187.  
  188.    case MAXALLOC:
  189.       msg = "  Any key to continue... ";
  190.       break;
  191.  
  192.    default:
  193.       msg = "\n(A)bort, (E)xit, (I)gnore ? ";
  194.   }
  195.   if ( msg ) fprintf( stderr, msg );
  196.   flushall();
  197.  }
  198.  
  199.  if ( delimiter == NOTFREEED ) return;
  200.  
  201.  blocks.inError = 0;
  202.  
  203.  /* if standard input is redirected, redirect it to console */
  204.  {
  205.   union REGS regs;
  206.   regs.h.ah = 0x44;    /* IOCTL function   */
  207.   regs.h.al = 0x00;    /* subfunction  */
  208.   regs.x.bx = fileno(stdin);
  209.   intdos( ®s, ®s );
  210.   /* 0x80 -> character device;  0x01 -> stdin */
  211.   if ( !(regs.x.dx & 0x80) || !(regs.x.dx & 0x01) )
  212.      {
  213.       inputTell = ftell( stdin );
  214.       input = fdopen( dup(fileno(stdin)), "rt" );
  215.       freopen( "CON", "rt", stdin );
  216.      }
  217.  }
  218.  
  219.  switch( toupper(getche()) )
  220.  {
  221.   case 'A': if ( delimiter != MAXALLOC ) abort();
  222.   case 'E': if ( delimiter != MAXALLOC ) exit(-1);
  223.   case  0 : getch();
  224.  }
  225.  
  226.  blocks.inError = 1;
  227.  fputs( "\n", stderr );
  228.  
  229.  flushall();
  230.  if ( input )
  231.     {
  232.      /* restore original redirected stdin */
  233.      fclose( stdin );
  234.      fdopen( dup(fileno(input)), "rt" );
  235.      if ( inputTell >= 0 ) fseek( stdin, inputTell, SEEK_SET );
  236.      fclose( input );
  237.     }
  238.  
  239.  blocks.inError = 0;
  240. }
  241.  
  242.  
  243.  
  244. /* Check integrity of a block */
  245. static long check_blk( uchar *blk )
  246. {
  247.  Header *header1, *header2;
  248.  int i;
  249.  
  250.  /* Check leading block */
  251.  header1 = (Header *) blk;
  252.  if ( header1->nb1 != header1->nb2 )
  253.     {       
  254.      /* Do not know the exact number */
  255.      msg_print( -1, 0, header1 );
  256.      return -1;
  257.     }
  258.  
  259.  if ( header1->size1 != header1->size2 )
  260.     {
  261.      msg_print( header1->nb1, 0, header1 );
  262.      return -1;
  263.     }
  264.  
  265.  for ( i = 0; i < pad_size; i++ )
  266.      if ( header1->padding[i] != BLK_DATA )
  267.         {
  268.          msg_print( header1->nb1, 0, header1 );
  269.          return -1;
  270.         }
  271.  
  272.  header2 = (Header *) (blk + header1->size1 - sizeof(Header) );
  273.  
  274.  /* Check trailing block */
  275.  if ( memcmp(header1, header2, sizeof(Header)) )
  276.     {
  277.      msg_print( header1->nb1, 1, header2 );
  278.      return -1;
  279.     }
  280.  
  281.  return (long) header1->nb1;
  282. }
  283.  
  284.  
  285. /* Loop on all allocated blocks */
  286. void allocCheck( void )
  287. {
  288.  size_t blk_nb;
  289.  
  290.  if ( blocks.inError ) return;
  291.  
  292.  if ( ! heapOK() )
  293.     {
  294.      msg_print( BADHEAP, BADHEAP, NULL );
  295.      return;
  296.     }
  297.  
  298.  for ( blk_nb = 0; blk_nb < blocks.total; blk_nb++ )
  299.      check_blk( blocks.contents[blk_nb] );
  300. }
  301.  
  302.  
  303. /* internal function to write headers inside a block */           
  304. static void buildHeader( void *blk, size_t size, size_t blk_nb )
  305. {
  306.  Header *header;
  307.  
  308.  /* Construct leading block */
  309.  header = (Header *) blk;
  310.  header->size1 = header->size2 = size;
  311.  header->nb1 = header->nb2 = blk_nb;
  312.  memset( header->padding, BLK_DATA, pad_size ) ;
  313.  
  314.  /* Construct trailing block */
  315.  header = (Header *) ((uchar*)blk + size - sizeof(Header) );
  316.  header->size1 = header->size2 = size;
  317.  header->nb1 = header->nb2 = blk_nb;
  318.  memset( header->padding, BLK_DATA, pad_size ) ;
  319. }
  320.  
  321.  
  322. void checkNotFreeed( void )
  323. {
  324.  if ( blocks.total ) msg_print( NOTFREEED, NOTFREEED, NULL );
  325.  msg_print( MAXALLOC, MAXALLOC, NULL );
  326. }
  327.  
  328.  
  329. /* Overwrite RTL function */
  330. void *malloc( size_t size )
  331. {
  332.  uchar *blk;
  333.  size_t blk_nb;
  334.  static int done = 0;
  335.  
  336.  if ( ! done )
  337.     {
  338.      atexit( checkNotFreeed );
  339.      done = 1;
  340.     }
  341.  
  342.  allocCheck();
  343.  
  344.  /* add the place needed to store the two headers */
  345.  size += 2 * sizeof(Header);
  346.  
  347.  /* call the RTL malloc() in its far version */
  348.  blk = (uchar *) _fmalloc( size );
  349.  
  350.  if ( ! blk ) return NULL;
  351.  
  352.  /* Store block in the table */
  353.  if ( blocks.total < MAXSIZE )
  354.     {
  355.      blk_nb = blocks.total++;
  356.      blocks.contents[blk_nb] = blk;
  357.     }
  358.     else blk_nb = MAXSIZE + 1;  /* no storage */
  359.  
  360.  /* Construct headers */
  361.  buildHeader( blk, size, blk_nb );
  362.  
  363.  /* update maximum allocated bytes */
  364.  blocks.totalAlloc += size - 2 * sizeof(Header);
  365.  blocks.maxAlloc = max( blocks.maxAlloc, blocks.totalAlloc );
  366.  
  367.  /* return to the user the block after the header. */
  368.  return blk + sizeof(Header);
  369. }
  370.  
  371.  
  372.  
  373. /* Overwrite RTL function */
  374. void *realloc( void *block, size_t size )
  375. {
  376.  uchar *blk;
  377.  Header *header;
  378.  
  379.  allocCheck();
  380.  
  381.  /* add the place needed to store the two headers */
  382.  size += 2 * sizeof(Header);
  383.                                       
  384.  /* real block has a header before the block the user knows about. */
  385.  blk = (uchar *) block - sizeof(Header);                             
  386.  
  387.  /* call the RTL realloc() in its far version */
  388.  blk = (uchar *) _frealloc( blk, size );
  389.  
  390.  if ( ! blk ) return NULL;
  391.  
  392.  /* update maximum allocated bytes */
  393.  blocks.totalAlloc += (ulong)size - (ulong)((Header*)blk)->size1;
  394.  blocks.maxAlloc = max( blocks.maxAlloc, blocks.totalAlloc );
  395.  
  396.  /* Construct headers */
  397.  header = (Header *)blk;
  398.  buildHeader( blk, size, header->nb1 );
  399.  
  400.  /* Store block in the table (at the same place as before */
  401.  if ( header->nb1 < MAXSIZE )
  402.     blocks.contents[header->nb1] = blk;
  403.  
  404.  /* return to the user the block after the header. */
  405.  return blk + sizeof(Header);
  406. }
  407.  
  408.  
  409.  
  410. /* Overwrite RTL function */
  411. void free( void *block )
  412. {
  413.  long blk_nb;
  414.  Header *header;
  415.  
  416.  /* real block has a header before the block the user knows about. */
  417.  uchar *blk = (uchar *)block - sizeof(Header);
  418.  
  419.  allocCheck();
  420.  
  421.  if ( ! block )
  422.     {
  423.      /*
  424.         the next line may be suppressed to avoid
  425.         messages about freeing NULL pointers
  426.      */
  427.      msg_print( 0, 0, NULL );
  428.      return;
  429.     }
  430.  
  431.  blocks.inFree = 1;          /* flag to see if we are checking the block to be freeed */
  432.  
  433.  /* block to be freeed may not be in table (if invalid or table is full) */
  434.  blk_nb = check_blk( blk );
  435.  
  436.  blocks.inFree = 0;
  437.  if ( blk_nb < 0 ) return;   /* error but user chose to ignore it (but we don't free) */
  438.  
  439.  /* update maximum allocated bytes */
  440.  blocks.totalAlloc -= ((Header*)blk)->size1 - 2 * sizeof(Header);
  441.  
  442.  /* overwrite block header (if we try to free it again) */
  443.  header = (Header *) blk;
  444.  memset( blk + header->size1 - sizeof(Header), '\xFF', sizeof(Header) );
  445.  memset( blk, '\xFF', sizeof(Header) );
  446.  
  447.  /*
  448.  Remove block from the table and replace it with the last one
  449.  (to not keep holes in the table).
  450.  */
  451.  if ( blk_nb < MAXSIZE )
  452.     {
  453.      blocks.total--;
  454.      if ( blk_nb != (long)blocks.total )
  455.         {
  456.          blocks.contents[(size_t)blk_nb] = blocks.contents[blocks.total];
  457.          header = (Header *) blocks.contents[(size_t)blk_nb];
  458.          header->nb1 = header->nb2 = (size_t)blk_nb;
  459.          header = (Header *) (blocks.contents[(size_t)blk_nb] + header->size1 - sizeof(Header) );
  460.          header->nb1 = header->nb2 = (size_t)blk_nb;
  461.         }
  462.      blocks.contents[blocks.total] = 0;
  463.     }
  464.  
  465.  /* call the RTL free() in its far version */
  466.  _ffree( blk );
  467. }
  468.